package com.itheima.stock.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.io.BaseEncoding;
import com.itheima.stock.constant.StockConstant;
import com.itheima.stock.mapper.SysPermissionMapper;
import com.itheima.stock.mapper.SysRoleMapper;
import com.itheima.stock.mapper.SysUserMapper;
import com.itheima.stock.mapper.SysUserRoleMapper;
import com.itheima.stock.pojo.domain.SysUserDomain;
import com.itheima.stock.pojo.entity.SysPermission;
import com.itheima.stock.pojo.entity.SysRole;
import com.itheima.stock.pojo.entity.SysUser;
import com.itheima.stock.pojo.entity.SysUserRole;
import com.itheima.stock.service.UserService;
import com.itheima.stock.utils.IdWorker;
import com.itheima.stock.utils.ParsePerm;
import com.itheima.stock.vo.req.*;
import com.itheima.stock.vo.resp.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.configurationprocessor.json.JSONException;
import org.springframework.boot.configurationprocessor.json.JSONObject;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author : itheima
 * @date : 2022/9/19 16:23
 * @description :
 */
@Slf4j
@Service("userService")
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private SysPermissionMapper sysPermissionMapper;

    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private IdWorker idWorker;


    /**
     * 根据用户名查询用户信息
     *
     * @param userName
     * @return
     */
    @Override
    public SysUser getUserByUserName(String userName) {
        return sysUserMapper.getUserByUserName(userName);
    }

    /**
     * 用户登录功能
     *
     * @param vo
     * @return
     */
    @Override
    public R<AccessTokenLoginRespVo> login(LoginReqVo vo) {
        //1.校验参数的合法性
        if (vo == null || StringUtils.isBlank(vo.getUsername()) || StringUtils.isBlank(vo.getPassword())) {
            return R.error(ResponseCode.DATA_ERROR);
        }

        //2.校验验证码和sessionId是否有效
        if (StringUtils.isBlank(vo.getCode()) || StringUtils.isBlank(vo.getSessionId())) {
            return R.error(ResponseCode.DATA_ERROR);
        }

        //3.根据key从redis中获取缓存的校验码
        String redisCode = (String) redisTemplate.opsForValue().get(StockConstant.CHECK_PREFIX + vo.getSessionId());
        //判断获取的验证码是否存在，以及是否与输入的验证码相同
        if (StringUtils.isBlank(redisCode)) {
            // 验证码过期
            return R.error(ResponseCode.CHECK_CODE_TIMEOUT);
        }
        if (!redisCode.equalsIgnoreCase(vo.getCode())) {
            //验证码输入有误
            return R.error(ResponseCode.CHECK_CODE_ERROR);
        }

        //4.根据账户名称去数据库查询获取用户信息
        SysUser dbUser = sysUserMapper.getUserByUserName(vo.getUsername());

        //5.判断数据库用户是否存在
        if (dbUser == null) {
            return R.error(ResponseCode.ACCOUNT_NOT_EXISTS);
        }

        //6.如果存在，则获取密文密码，然后传入的明文进行匹配,判断是否匹配成功
        if (!passwordEncoder.matches(vo.getPassword(), dbUser.getPassword())) {
            return R.error(ResponseCode.USERNAME_OR_PASSWORD_ERROR);
        }

        //7.正常响应
        LoginRespVo respVo = new LoginRespVo();
        BeanUtils.copyProperties(dbUser, respVo);

        List<SysPermission> perms = sysPermissionMapper.getPermsByUserId(dbUser.getId());
        respVo.setPermissions(ParsePerm.getPermissions(perms));
        respVo.setMenus(ParsePerm.recursion(perms, 0L));
        AccessTokenLoginRespVo accessTokenLoginRespVo = new AccessTokenLoginRespVo();
        BeanUtils.copyProperties(respVo, accessTokenLoginRespVo);
        String info = accessTokenLoginRespVo.getId() + ":" + accessTokenLoginRespVo.getUsername();
        accessTokenLoginRespVo.setAccessToken(BaseEncoding.base64().encode(info.getBytes()));
        return R.ok(accessTokenLoginRespVo);
    }

    /**
     * 登录校验吗方法实现
     *
     * @return
     */
    @Override
    public R<Map> getCaptchaCode() {
        // 1.生成图片校验码
        /*
            参数1：图片宽度
            参数2：图片高度
            参数3：图片中包含验证码长度
            参数4：干扰线数量
         */
        LineCaptcha captcha = CaptchaUtil.createLineCaptcha(250, 40, 4, 5);
        //设置背景颜色清灰
        captcha.setBackground(Color.lightGray);
        //自定义校验码生成方式
//        captcha.setGenerator(new CodeGenerator() {
//            @Override
//            public String generate() {
//                return RandomStringUtils.randomNumeric(4);
//            }
//            @Override
//            public boolean verify(String code, String userInputCode) {
//                return code.equalsIgnoreCase(userInputCode);
//            }
//        });
        //获取图片中的验证码，默认生成的校验码包含文字和数字，长度为4
        String checkCode = captcha.getCode();
        // 2.生成 sessionId 转化成 string，避免前端精度丢失
        String sessionId = String.valueOf(idWorker.nextId());
        log.info("生成校验码: {}, 会话id: {}", checkCode, sessionId);
        // 3.将sessionId（key）和校验码(value)保存在redis下，并设置缓存中数据存活时间一分钟
        //   (使用redis模拟session行为，通过过期时间设置)
        redisTemplate.opsForValue().set(StockConstant.CHECK_PREFIX + sessionId, checkCode, 1, TimeUnit.MINUTES);
        // 4.组装响应数据
        HashMap<String, String> info = new HashMap<>();
        info.put("sessionId", sessionId);
        info.put("imageData", captcha.getImageBase64());//获取base64格式的图片数据
        //设置响应数据格式
        return R.ok(info);
    }

    /**
     * 多条件综合查询用户分页信息，条件包含：分页信息 用户创建日期范围
     *
     * @param reqVo
     * @return
     */
    @Override
    public R<PageResult<SysUserDomain>> getUserInfosByMCondition(UserMConditionReqVo reqVo) {
        PageHelper.startPage(reqVo.getPageNum(), reqVo.getPageSize());
        List<SysUserDomain> all = sysUserMapper.getUserInfosByMCondition(reqVo.getUsername(), reqVo.getNickName(), reqVo.getStartTime(), reqVo.getEndTime());
        if (CollectionUtils.isEmpty(all)) {
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        PageInfo<SysUserDomain> pageInfo = new PageInfo<>(all);
        PageResult<SysUserDomain> pageResult = new PageResult<>(pageInfo);
        return R.ok(pageResult);
    }

    /**
     * 添加用户信息  服务路径：/api/user
     *
     * @param reqVo
     * @return
     */
    @Override
    public R addUser(SysUserAddReqVo reqVo, String accessToken) {
        String info = new String(BaseEncoding.base64().decode(accessToken));
        SysUser dbUser = sysUserMapper.getUserByUserName(reqVo.getUsername());
        if (dbUser != null) {
            return R.error(ResponseCode.ACCOUNT_EXISTS_ERROR);
        }
        SysUser user = new SysUser();
        BeanUtils.copyProperties(reqVo, user);
        user.setId(idWorker.nextId());
        user.setCreateId(Long.parseLong(info.split(":")[0]));
        user.setCreateTime(DateTime.now().toDate());
        int i = sysUserMapper.addUser(user);
        if (i > 0) {
            return R.ok();
        } else
            return R.error(ResponseCode.ERROR.getMessage());
    }

    /**
     * 获取用户具有的角色信息，以及所有角色信息
     *
     * @param userId
     */
    @Override
    public R<Map<String, List>> getUserRolesAndAllRoles(String userId) {
        HashMap<String, List> map = new HashMap<>();
        List<String> userRolesIds = sysUserRoleMapper.getRolesIdByUserId(userId);
        List<SysRole> roles = sysRoleMapper.selectAll();
        if (CollectionUtils.isEmpty(roles)) {
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        map.put("ownRoleIds", userRolesIds);
        map.put("allRole", roles);
        return R.ok(map);
    }

    /**
     * 更新用户角色信息
     *
     * @param reqVo
     * @return
     */
    @Override
    public R updateUserRoles(UpdateUserRolesReqVo reqVo) {
        List<String> rolesList = sysUserRoleMapper.getRolesIdByUserId(String.valueOf(reqVo.getUserId()));
        if (!CollectionUtils.isEmpty(rolesList)) {
            int i = sysUserRoleMapper.deleteUserRolesByUserId(reqVo.getUserId());
            if (i <= 0) {
                return R.error(ResponseCode.DATA_ERROR.getMessage());
            }
        }
        List<SysUserRole> userRoles = new ArrayList<>();
        for (Long roleId : reqVo.getRoleIds()) {
            userRoles.add(SysUserRole.builder().id(idWorker.nextId()).userId(reqVo.getUserId())
                    .roleId(roleId).createTime(DateTime.now().toDate()).build());
        }
        int j = sysUserRoleMapper.updateUserRoles(userRoles);
        if (j > 0) {
            return R.ok();
        } else return R.error(ResponseCode.DATA_ERROR.getMessage());
    }

    /**
     * 批量删除用户信息，delete请求可通过请求体携带数据
     *
     * @param userIds
     * @return
     */
    @Override
    public R deleteUsers(List<Long> userIds) {
        int i = sysUserMapper.deleteUsersById(userIds);
        if (i > 0) {
            return R.ok();
        } else
            return R.error(ResponseCode.ERROR.getMessage());
    }

    /**
     * 根据用户id查询用户信息
     *
     * @param id
     * @return
     */
    @Override
    public R<UserInfoRespVo> selectUserInfoById(String id) {
        SysUser dbUser = sysUserMapper.selectByPrimaryKey(Long.parseLong(id));
        if (dbUser != null) {
            UserInfoRespVo respVo = new UserInfoRespVo();
            BeanUtils.copyProperties(dbUser, respVo);
            return R.ok(respVo);
        } else {
            return R.error(ResponseCode.ERROR.getMessage());
        }

    }

    /**
     * 根据id更新用户基本信息
     *
     * @param reqVo
     * @return
     */
    @Override
    public R updateUserInfo(SysUserUpdateReqVo reqVo, String accessToken) {
        // 分割JWT为它的三个组成部分
        String[] parts = accessToken.split("\\.");
        String info = new String(BaseEncoding.base64().decode(parts[1].replace(".", "").replace("_", "").replace("-", "")));

        String username = null;
        try {
            // 解析JSON并获取username字段
            JSONObject jsonPayload = new JSONObject(info);
            username = jsonPayload.getString("username");
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }

        SysUser dbUser = new SysUser();
        BeanUtils.copyProperties(reqVo, dbUser);
        log.info("info: {}", info);
        log.info("split info: {}", username);
        SysUser updateUser = sysUserMapper.getUserByUserName(username);
        dbUser.setUpdateId(updateUser.getId());
        int i = sysUserMapper.updateByPrimaryKeySelective(dbUser);
        if (i > 0) {
            return R.ok();
        } else
            return R.error(ResponseCode.ERROR.getMessage());
    }
}
